package panda.record;

import panda.file.Block;
import panda.file.Page;
import panda.transaction.Transaction;

public class RecordPage {
	public static final int NULL_BIT = 1, INUSE_BIT = 0;

	public static final int NULL = 1, NOT_NULL = 0, EMPTY = 0, INUSE = 1;

	private static final int INT_SIZE = Integer.SIZE / Byte.SIZE;

	private int currentPosition = INT_SIZE * 2;

	private TableToken tableToken;

	private Block block;

	private Transaction transaction;

	private int recordSize;

	public RecordPage(Block block, TableToken tableToken,
			Transaction transaciton) {
		this.block = block;
		this.tableToken = tableToken;
		this.transaction = transaciton;
		transaciton.pin(block);
		recordSize = tableToken.getTupleLength() + INT_SIZE * 2;
		currentPosition -= recordSize;
		// TODO Please correct the following codes
		// Record page does not represents a material page so that not every new
		// of RecordPage is a request for new spaces
		// transaction.setInt(block, 0, recordSize-1);
		// int offset = recordSize-1;
		// while (offset + 2*recordSize < Block.BLOCK_SIZE) {
		// transaction.setInt(block, offset+INT_SIZE, offset + recordSize);
		// offset += recordSize;
		// }
	}

	/**
	 * Move to the specified place of this page
	 * 
	 * @param offset
	 */
	public void moveTo(int offset) {
		currentPosition = offset;
	}

	/**
	 * Move the cursor to the next tuple
	 * 
	 * @return false if end of file is reached;
	 */
	// TODO Please complete this method
	public boolean next() {
		// if ((currentPosition + 1)%recordSize!=0){
		// System.out.println("haha");
		// }
		currentPosition += recordSize;
		while (currentPosition + recordSize <= Block.BLOCK_SIZE) {
			if (transaction.getInt(block, currentPosition) % 2 == INUSE) {
				return true;
			}
			currentPosition += recordSize;
		}
		return false;
	}

	/**
	 * Close this scanner
	 */
	public void close() {
		if (block != null)
			transaction.unpin(block);
		block = null;
	}

	public int getInt(String fieldName) {
		int position = getPosition(fieldName);
		return transaction.getInt(block, position);
	}

	public String getString(String fieldName) {
		int position = getPosition(fieldName);
		return transaction.getString(block, position);
	}

	public float getFloat(String fieldName) {
		int position = getPosition(fieldName);
		return transaction.getFloat(block, position);
	}

	public long getLong(String fieldName) {
		int position = getInt(fieldName);
		return transaction.getLong(block, position);
	}

	public byte[] getArray(String fieldName) {
		int position = getInt(fieldName);
		return transaction.getArray(block, position);
	}

	public boolean isNull(String fieldName) {
		return getNthBit(transaction.getInt(block, currentPosition), tableToken
				.getSchema().IDof(fieldName));
	}

	public void setInt(String fieldName, int value) {
		int position = getPosition(fieldName);
		transaction.setInt(block, position, value);
		clearNull(fieldName);
	}

	public void setString(String fieldName, String value) {
		// System.out.println(" " + value);
		// System.out.println(fieldName);
		int position = getPosition(fieldName);
		transaction.setString(block, position, value);
		clearNull(fieldName);
	}

	public void setFloat(String fieldName, float value) {
		int position = getPosition(fieldName);
		transaction.setFloat(block, position, value);
		clearNull(fieldName);
	}

	public void setLong(String fieldName, long value) {
		int position = getPosition(fieldName);
		transaction.setLong(block, position, value);
		clearNull(fieldName);
	}

	public void setArray(String fieldName, byte[] value) {
		int position = getPosition(fieldName);
		transaction.setArray(block, position, value);
		clearNull(fieldName);
	}

	public void setNull(String fieldName) {
		transaction.setInt(block, currentPosition, setNthBit(transaction
				.getInt(block, currentPosition), tableToken.getSchema().IDof(
				fieldName)));
	}

	public void clearNull(String fieldName) {
		transaction.setInt(block, currentPosition, clearNthBit(transaction
				.getInt(block, currentPosition), tableToken.getSchema().IDof(
				fieldName)));
	}

	private int getPosition(String fieldName) {
		int offset = INT_SIZE * 2 + tableToken.offsetOf(fieldName);
		// System.out.println(recordSize);
		// System.out.println(offset);
		return currentPosition + offset;
	}

	public void delete() {
		/*
		 * int position = currentPosition; transaction.setInt(block, position,
		 * newFlag(EMPTY));
		 */
		int position = currentPosition;
		int newPosition = transaction.getInt(block, 0);
		transaction.setInt(block, position, 0);
		transaction.setInt(block, position + Page.INT_SIZE, newPosition);
		transaction.setInt(block, 0, position);
	}

	public boolean insert() {
		/*
		 * currentPosition = -recordSize; while (currentPosition + recordSize <=
		 * Block.BLOCK_SIZE) { currentPosition += recordSize; if
		 * (transaction.getInt(block, currentPosition) % 2 == EMPTY) { break; } }
		 * if (transaction.getInt(block, currentPosition) % 2 == EMPTY) {
		 * transaction.setInt(block, currentPosition, newFlag(INUSE)); return
		 * true; } return false;
		 */
		int position = transaction.getInt(block, 0);
		if (position == 0)
			return false;
		// if (position == 47)
		// System.out.println();
		transaction.setInt(block, 0, transaction.getInt(block, position
				+ INT_SIZE));
		currentPosition = position;
		// System.out.println("yoyoyo" + currentPosition);
		// System.out.println("jojojo" + recordSize);
		transaction.setInt(block, currentPosition, newFlag(INUSE));
		return true;
	}

	private int newFlag(int isEmpty) {
		int returnValue = isEmpty;
		// System.out.println(tableToken.getFieldCount());
		for (int i = 1; i < tableToken.getSchema().getFieldCount(); i++) {
			setNthBit(returnValue, i);
		}
		return returnValue;
	}

	public boolean getNthBit(int flag, int n) {
		int NthBit = (flag >> n % 2);
		return (NthBit == 1);
	}

	public int setNthBit(int flag, int n) {
		return flag | (1 << n);
	}

	public int clearNthBit(int flag, int n) {
		if (getNthBit(flag, n)) {
			return flag ^ (1 << n);
		} else
			return flag;
	}

	public int getCurrentPosition() {
		return currentPosition;
	}

	public int nextAvailablePage() {
		return transaction.getInt(block, Page.INT_SIZE);
	}

	public int removeFromAvailableChain() {
		int returnValue = transaction.getInt(block, Page.INT_SIZE);
		transaction.setInt(block, Page.INT_SIZE, 0);
		return returnValue;
	}

	public int addToChain(int blockNumber) {
		transaction.setInt(block, Page.INT_SIZE, blockNumber);
		return block.getBlockNumber();
	}

	public boolean isAvailable() {
		return (transaction.getInt(block, 0) != 0);
	}

	public int getAvailableChainPointer() {
		return transaction.getInt(block, Page.INT_SIZE);
	}

	public void setAvailableChainPointer(int pointer) {
		transaction.setInt(block, Page.INT_SIZE, pointer);
	}
	
	public int startPosition(){
		return INT_SIZE*2-recordSize;
	}
	
}
